package ffte.types

import breeze.math.Complex
import spinal.core._
import spinal.lib._

import ffte.property.{getResolution}

/**
 * Vector of complex numbers for parallel FFT operations
 *
 * This class implements a vector (array) of fixed-point complex numbers as a SpinalHDL Bundle.
 * It is optimized for parallel FFT operations and provides comprehensive vector arithmetic,
 * manipulation, and transformation operations essential for FFT hardware implementations.
 *
 * Data Representation:
 * - Vector of FixComplex elements with uniform size and precision
 * - Configurable vector size, element bit width, and fractional resolution
 * - Hardware-friendly parallel operations on all elements
 * - Support for various FFT-specific data transformations
 *
 * Key Features:
 * - Vector arithmetic operations (element-wise +, -, *, unary minus)
 * - FFT-specific transformations (bit-reversal, rotation, subdivision)
 * - Precision management (cut, fixTo, resize, expand)
 * - Special operations (conjugate, j-multiplication, swapping)
 * - Conversion and initialization utilities
 *
 * FFT Applications:
 * - Parallel butterfly operations in FFT cores
 * - Twiddle factor application across multiple data points
 * - Data reordering for different FFT algorithms
 * - Vectorized complex arithmetic for computational efficiency
 *
 * @param size Number of complex elements in the vector
 * @param width Bit width for each complex element
 * @param resolution Fractional bit resolution (defaults to global setting)
 */
case class VecComplex(val size:Int, val width:Int, val resolution:Int=getResolution()) extends Bundle {
    /** Vector of FixComplex elements */
    val d = Vec(FixComplex(width,resolution),size)
    /** Create a duplicate vector with same parameters */
    def dup = VecComplex(size,width,resolution)
    /**
     * Expand bit width of all elements
     *
     * Creates a new vector where each complex element has expanded bit width
     * to prevent overflow in addition operations.
     *
     * @return New VecComplex with expanded element bit width
     */
    def expand = {
        val r = VecComplex(size,width+1,resolution)
        for (i <- 0 until size) r.d(i) := this.d(i).expand
        r
    }
    /**
     * Element-wise vector addition
     *
     * Performs element-wise addition of two complex vectors.
     * Both vectors must have the same size.
     *
     * @param right Addend vector
     * @return Element-wise sum vector
     */
    def + (that: VecComplex): VecComplex = {
        val r = this.dup
        for (i <- 0 until size) r.d(i) := this.d(i) + that.d(i)
        r
    }    
    def - (that: VecComplex): VecComplex = {
        val r = this.dup
        for (i <- 0 until size) r.d(i) := this.d(i) - that.d(i)
        r
    }    
    def :=(that: VecComplex): Unit     = {
        for (i <- 0 until size) d(i) := that.d(i)
    }
    def * (that: VecComplex): VecComplex = {
        assert(size == that.size,s"mul vec complex must be same size.")
        val r = VecComplex(this.size,this.width+that.width,this.resolution+that.resolution)
        for (i <- 0 until size) r.d(i) := this.d(i) * that.d(i)
        r
    }
    def * (that: FixReal): VecComplex = {
        val r = VecComplex(this.size,this.width+that.width,this.resolution+that.resolution)
        for (i <- 0 until size) r.d(i) := this.d(i) * that
        r
    }
    def * (that: FixComplex): VecComplex = {
        val r = VecComplex(this.size,this.width+that.width,this.resolution+that.resolution)
        for (i <- 0 until size) r.d(i) := this.d(i) * that
        r
    }
    def unary_- : VecComplex = {
        val r = this.dup
        for (i <- 0 until size) r.d(i) := -this.d(i)
        r
    }
    def j = {
        val r = this.dup
        for (i <- 0 until size) r.d(i) := this.d(i).j
        r
    }
    def conj = {
        val r = this.dup
        for (i <- 0 until size) r.d(i) := this.d(i).conj
        r
    }    
    def cut(w:Int,s:Int) = {
        val r = VecComplex(size,w,resolution)
        for (i <- 0 until size) r.d(i) := this.d(i).cut(w,s)
        r
    }
    def cut(w:Int,s:Int,r:Int) = {
        val ret = VecComplex(size,w,r)
        for (i <- 0 until size) ret.d(i) := this.d(i).cut(w,s,r)
        ret
    }
    def one = {
        val r = this.dup
        for (i <- 0 until size) r.d(i) := this.d(i).one
        r
    }
    def sat(m:Int) = {
        val r = this.dup
        for (i <- 0 until size) r.d(i) := this.d(i).sat(m)
        r
    }
    def expj(m:Int,N:Int) = {
        val r = this.dup
        for (i <- 0 until size) r.d(i) := this.d(i).expj(m,N)
        r    
    }
    def fromComplex(x:Complex) = {
        for (i <- 0 until size) this.d(i).fromComplex(x)
        this
    }
    def fixTo(w:Int,s:Int) = {
        val ret = VecComplex(size,w,resolution)
        for (i <- 0 until size) {
            ret.d(i) := this.d(i).fixTo(w,s)
        }
        ret
    }
    def fixTo(w:Int,s:Int,r:Int) = {
        val ret = VecComplex(size,w,r)
        for (i <- 0 until size) {
            ret.d(i) := this.d(i).fixTo(w,s,r)
        }
        ret
    }
    def subdivideIn(ss:Int) = {
        val ret = Vec(VecComplex(ss,width,resolution),size/ss)
        for (i <- 0 until size) {
            val sss = size/ss
            val n = i%sss
            val k = i/sss
            ret(n).d(k) := d(i) 
        }
        ret
    }
    def sW = log2Up(size)
    def reverse_tab = {
        (for (i <- 0 until size) yield {
            var x = 0
            for(j <- 0 until sW) {
                x |= ((i>>j)&1)<<(sW-j-1)
            }
            x
        }).toArray
    }
    /**
     * Bit-reversal permutation
     *
     * Reorders vector elements using bit-reversal of indices.
     * Essential for decimation-in-time (DIT) FFT algorithms.
     * Vector size must be a power of 2.
     *
     * @return Bit-reversed ordered vector
     */
    def reverse_order_bits = {
        assert(size == (1<<sW),"reverse order must be 2^n")
        val r = this.dup
        for (i <- 0 until size) r.d(i) := this.d(reverse_tab(i))
        r
    }
    def rotate(n:Int) = {
        val r = this.dup
        for(i<-n until size) r.d(i-n)      := this.d(i)
        for(i<- 0 until n)   r.d(size+i-n) := this.d(i)
        r
    }
    def swap = {
        val r = this.dup
        for(i<-0 until size) r.d(i)      := this.d(i).swap
        r
    }
    def resize(dW:Int) = {
        val r = new VecComplex(size,dW,resolution)
        for(i<-0 until size) r.d(i)      := this.d(i).resize(dW)
        r
    }
    def zero = {
        val r = this.dup
        for(i<-0 until size) r.d(i)      := this.d(i).zero
        r
    }
}